package main

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"
)

// Server 服务器结构体
type Server struct {
	IP   string
	Port int

	// 在线用户列表
	OnlineMap map[string]*User
	mapLock   sync.RWMutex // 读写锁：多读单写，因为go map不是线程安全的，可能出现并发问题

	// 消息广播的channel
	Message chan string
}

// 创建server接口
func createServer(IP string, Port int) *Server {
	return &Server{
		IP:        IP,
		Port:      Port,
		OnlineMap: make(map[string]*User),
		Message:   make(chan string),
	}
}

// BroadCast 广播方法
func (s *Server) BroadCast(user *User, message string) {
	msg := "【" + user.Name + "（" + user.Addr + "）】：" + message
	s.Message <- msg
}

// 监听广播的goroutine，一旦有消息，则发送给所有在线user
func (s *Server) ListenMessage() {
	for {
		msg := <-s.Message
		// 循环发送消息给所有在线用户
		s.mapLock.Lock()
		for _, cli := range s.OnlineMap {
			cli.C <- msg
		}
		s.mapLock.Unlock()
	}
}

// server处理接口
func (s Server) handler(conn net.Conn) {
	// 当前链接的业务
	// fmt.Println("链接建立成功")

	// 用户上线
	user := NewUser(conn, &s)
	user.start()

	// 广播当前用户的上线消息
	user.Online()

	// 监听用户是否活跃的channel
	isLive := make(chan bool)

	// 接收用户客户端消息的goroutine
	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := conn.Read(buf) // n表示成功读取的长度
			if n == 0 {
				user.Offline()
				return
			}
			if err != nil && err != io.EOF {
				fmt.Println("Conn read err：", err)
				return
			}

			// 提取用户的消息（去除\n）
			msg := string(buf[:n-1])
			// 广播用户消息
			user.DoMessage(msg)

			// 用户的任意消息，代表用户活跃
			isLive <- true
		}
	}()

	// 阻塞当前handler
	for {
		select {
		case <-isLive:
			// 当前用户是活跃的
			// 不需要处理，激活select重置定时器（执行time.After(10 * time.Second)）
		case <-time.After(5 * time.Minute):
			// 每次循环都会重置时间
			// 超过10s时触发，将用户踢下线
			user.sendMsg("超时登录，已被强踢\n")
			// 销毁用户资源
			close(user.C)
			conn.Close()
			// 退出
			return
			//runtime.Goexit()
		}
	}
}

// 启动服务器的接口
func (s *Server) start() {
	// 1.监听socket
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.IP, s.Port))
	if err != nil {
		fmt.Println("net listen err:", err)
		return
	}
	defer listener.Close() // 4.关闭socket，提前写在此处

	// 监听Message的goroutine
	go s.ListenMessage()

	for {
		// 2.accpet
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("listen accept err:", err)
			continue
		}
		// 3.handler
		go s.handler(conn)
	}
}
